package org.dromara.system.config.core;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.system.config.redis.RedisDelayMQMessage;
import org.dromara.system.enums.NotificationEnum;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Redis延时队列消息发送器
 *
 * @author lilu
 */
@Slf4j
@Component
public class RedisDelayMQSender {

    private final RedisTemplate redisTemplate;

    public RedisDelayMQSender(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 发送延迟消息，一次性消费
     *
     * @param queueName     队列名称
     * @param message       消息体
     * @param delayTime     延迟时间
     * @param delayTimeUnit 延迟时间单位
     */
    public void send(String queueName, Serializable message, long delayTime, TimeUnit delayTimeUnit) {
        send(queueName, message, delayTime, delayTimeUnit, 0, 0, null);
    }

    /**
     * 发送延迟消息，重复消费直到消费成功为止
     *
     * @param queueName              队列名称
     * @param message                消息体
     * @param delayTime              延迟时间
     * @param delayTimeUnit          延迟时间单位
     * @param failRepeatInterval     消费失败重复消费间隔时间
     * @param failRepeatIntervalUnit 消费失败重复消费间隔时间单位
     */
    public void send(String queueName, Serializable message, long delayTime, TimeUnit delayTimeUnit, long failRepeatInterval, TimeUnit failRepeatIntervalUnit) {
        send(queueName, message, delayTime, delayTimeUnit, -1, failRepeatInterval, failRepeatIntervalUnit);
    }

    /**
     * 发送延迟消息
     *
     * @param queueName              队列名称
     * @param message                消息体
     * @param delayTime              延迟时间
     * @param delayTimeUnit          延迟时间单位
     * @param failRepeatNumber       消费失败重复次数 -1:不限, 0:不重复
     * @param failRepeatInterval     消费失败重复消费间隔时间
     * @param failRepeatIntervalUnit 消费失败重复消费间隔时间单位
     */
    public void send(String queueName, Serializable message, long delayTime, TimeUnit delayTimeUnit, int failRepeatNumber, long failRepeatInterval, TimeUnit failRepeatIntervalUnit) {
        if (StrUtil.isBlank(queueName) || Objects.isNull(message)) {
            return;
        }
        RedisDelayMQMessage redisDelayMQMessage = new RedisDelayMQMessage();
        redisDelayMQMessage.setQueueName(queueName);
        redisDelayMQMessage.setCreateTime(System.currentTimeMillis());
        redisDelayMQMessage.setFailNumber(0);
        redisDelayMQMessage.setFailRepeatNumber(failRepeatNumber);
        if (failRepeatInterval > 0 && Objects.nonNull(failRepeatIntervalUnit)) {
            redisDelayMQMessage.setFailRepeatInterval(TimeoutUtils.toMillis(failRepeatInterval, failRepeatIntervalUnit));
        } else {
            if (0 != failRepeatNumber) {
                redisDelayMQMessage.setFailRepeatInterval(TimeoutUtils.toMillis(2, TimeUnit.MINUTES));
            }
        }
        redisDelayMQMessage.setPayload(message);
        long rawTimeout = TimeoutUtils.toMillis(delayTime, delayTimeUnit);
        boolean result = redisTemplate.opsForZSet().add(queueName, redisDelayMQMessage, System.currentTimeMillis() + rawTimeout);
    }

    public void send(String queueName, Serializable message, NotificationEnum notificationEnum, String orderNo) {
        if (StrUtil.isBlank(queueName) || Objects.isNull(message)) {
            return;
        }
        RedisDelayMQMessage redisDelayMQMessage = new RedisDelayMQMessage();
        redisDelayMQMessage.setQueueName(queueName);
        redisDelayMQMessage.setCreateTime(System.currentTimeMillis());
        redisDelayMQMessage.setPayload(message);
        redisDelayMQMessage.setNotificationEnum(notificationEnum);
        redisDelayMQMessage.setOrderNo(orderNo);
        redisDelayMQMessage.setFailNumber(0);
        redisDelayMQMessage.setFailRepeatNumber(3);
        redisDelayMQMessage.setFailRepeatInterval(TimeoutUtils.toMillis(5, TimeUnit.SECONDS));

        long rawTimeout = TimeoutUtils.toMillis(0, TimeUnit.SECONDS);
        boolean result = redisTemplate.opsForZSet().add(queueName, redisDelayMQMessage, System.currentTimeMillis() + rawTimeout);
        log.info("send redis delay queue message, sending result:{}, queue name:{}, message content:{}", result, queueName, redisDelayMQMessage);
    }

}
